package xin.alum.aim.util;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.zip.*;

/**
 * 解压缩工具类
 *
 * @author alum 20160827
 */
@UtilityClass
public class GZipUtil {

    /**
     * 压缩方式
     */
    public enum GZip {
        GZIP, ZIP, NONE
    }

    /**
     * @param zip
     * @param text
     * @return
     */
    public String gzip(GZip zip, String text) {
        if (text == null || text.length() == 0 || zip == GZip.NONE) {
            return text;
        }
        switch (zip) {
            case ZIP:
                text = zip(text);
            case GZIP:
                text = gzip(text);
            default:
        }
        return text.replace("\r\n", "");
    }

    /**
     * @param zip
     * @param text
     * @return
     */
    public String ungzip(GZip zip, String text) {
        if (text == null || text.length() == 0 || zip == GZip.NONE) {
            return text;
        }
        switch (zip) {
            case ZIP:
                text = unzip(text);
            case GZIP:
                text = unGzip(text);
            default:
        }
        return text;
    }

    /**
     * @param zip
     * @param in
     * @return
     */
    public byte[] gzip(GZip zip, byte[] in) {
        if (in == null || in.length == 0 || zip == GZip.NONE) {
            return in;
        }
        switch (zip) {
            case ZIP:
                in = zip(in);
            case GZIP:
                in = gzip(in);
            default:
        }
        return in;
    }

    /**
     * @param zip
     * @param in
     * @return
     */
    public byte[] ungzip(GZip zip, byte[] in) {
        if (in == null || in.length == 0 || zip == GZip.NONE) {
            return in;
        }
        switch (zip) {
            case ZIP:
                in = unzip(in);
            case GZIP:
                in = unGzip(in);
            default:
        }
        return in;
    }

    /**
     * Proto对象GZip解压
     *
     * @param in
     * @return
     */
    public ByteBuf ungzip(GZip gZip, ByteBuf in) {
        ByteBuf out;
        try {//
            byte[] req = new byte[in.readableBytes()];
            in.readBytes(req);
            out = Unpooled.wrappedBuffer(GZipUtil.ungzip(gZip, req));
        } catch (Exception e) {
            out = in.copy();
            e.printStackTrace();
        }
        return out;
    }


    /**
     * 功能：使用gzip进行压缩，然后再用Base64进行编码
     *
     * @param text 待压缩字符串
     * @return 返回压缩后字符串
     * @author alum
     */
    @SuppressWarnings("restriction")
    private String gzip(String text) {
        if (text == null || text.length() == 0) {
            return text;
        }
        byte[] r = gzip(text.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(r);
    }

    @SneakyThrows
    @SuppressWarnings("restriction")
    private byte[] gzip(byte[] in) {
        if (in == null || in.length == 0) {
            return in;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gzip = new GZIPOutputStream(out);
        gzip.write(in);
        gzip.close();
        return out.toByteArray();
    }

    /**
     * <p>
     * Description:使用gzip进行解压缩
     * 先对压缩数据进行BASE64解码。再进行Gzip解压
     * </p>
     *
     * @param inStr 压缩字符串
     * @return 返回解压字符串
     */
    @SneakyThrows
    @SuppressWarnings("restriction")
    private String unGzip(String inStr) {
        if (inStr == null) {
            return null;
        }
        byte[] r = Base64.getDecoder().decode(inStr);
        return new String(unGzip(r));
    }


    /**
     * <p>
     * Description:使用gzip进行解压缩
     * 先对压缩数据进行BASE64解码。再进行Gzip解压
     * </p>
     *
     * @param in 压缩子节
     * @return 返回解压字符串
     */
    @SneakyThrows
    @SuppressWarnings("restriction")
    private byte[] unGzip(byte[] in) {
        if (in == null || in.length == 0) {
            return null;
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream inputStream = new ByteArrayInputStream(in);
        GZIPInputStream unGzip = new GZIPInputStream(inputStream);
        byte[] buffer = new byte[in.length];
        int offset = -1;
        while ((offset = unGzip.read(buffer)) != -1) {
            out.write(buffer, 0, offset);
        }
        unGzip.close();
        inputStream.close();
        return out.toByteArray();
    }

    /**
     * 使用zip进行文本压缩
     *
     * @param inStr 压缩前的文本
     * @return 返回压缩后的文本
     */
    @SuppressWarnings("restriction")
    private String zip(String inStr) {
        if (inStr == null || inStr.trim().length() == 0) {
            return inStr;
        }
        return Base64.getEncoder().encodeToString(zip(inStr.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 使用zip进行字节压缩
     *
     * @param in
     * @return
     */
    @SneakyThrows
    @SuppressWarnings("restriction")
    private byte[] zip(byte[] in) {
        if (in == null || in.length == 0) {
            return in;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ZipOutputStream zout = new ZipOutputStream(out);
        zout.putNextEntry(new ZipEntry("0"));
        zout.write(in);
        zout.closeEntry();
        zout.close();
        return out.toByteArray();
    }

    /**
     * 使用zip进行解压缩
     *
     * @param text 压缩后的文本
     * @return 解压后的文本
     */
    @SneakyThrows
    @SuppressWarnings("restriction")
    private String unzip(String text) {
        if (text == null || text.length() == 0) {
            return text;
        }
        return new String(unzip(Base64.getDecoder().decode(text)));
    }


    /**
     * 使用zip进行解压缩
     *
     * @param in 压缩后的字节
     * @return 解压后的字节
     */
    @SneakyThrows
    @SuppressWarnings("restriction")
    private byte[] unzip(byte[] in) {
        if (in == null || in.length == 0) {
            return in;
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream inputStream = new ByteArrayInputStream(in);
        ZipInputStream zin = new ZipInputStream(inputStream);
        zin.getNextEntry();
        byte[] buffer = new byte[in.length];
        int offset = -1;
        while ((offset = zin.read(buffer)) != -1) {
            out.write(buffer, 0, offset);
        }
        zin.close();
        inputStream.close();
        return out.toByteArray();
    }
}